﻿namespace NorthernLights
{
    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Net;
    using System.Text;

    /// <summary>
    /// LittleWatson class.
    /// </summary>
    /// <remarks>
    /// Text to user: Send application error reports automatically and anonymously to southernsun to help us improve the application.
    /// This code is an extension on what Andy Pennell made:
    /// http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on-windows-phone-7.aspx
    /// </remarks>
    public class LittleWatson : INotifyPropertyChanged
    {
        #region Fields
        private static readonly LittleWatson instance = new LittleWatson();
        private const string Filename = "LittleWatson.txt";
        private const string SettingsFilename = "LittleWatsonSettings.txt";
        private bool allowAnonymousHttpReporting;
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes static members of the LittleWatson class.
        /// </summary>
        static LittleWatson()
        {
        }

        /// <summary>
        /// Prevents a default instance of the LittleWatson class from being created.
        /// </summary>
        private LittleWatson()
        {
            this.allowAnonymousHttpReporting = this.GetSetting();
        }
        #endregion

        #region Event
        /// <summary>
        /// Property Changed event.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Properties
        /// <summary>
        /// Gets DataManager instance.
        /// </summary>
        public static LittleWatson Instance
        {
            get
            {
                return LittleWatson.instance;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether error reports are allowed to send anonymously to a http endpoint.
        /// </summary>
        public bool AllowAnonymousHttpReporting
        {
            get
            {
                return this.allowAnonymousHttpReporting;
            }

            set
            {
                this.allowAnonymousHttpReporting = value;
                this.SetSetting(this.allowAnonymousHttpReporting);
                this.NotifyPropertyChanged("AllowAnonymousHttpReporting");
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Report exception.
        /// </summary>
        /// <param name="ex">The exception to report.</param>
        /// <remarks>Won't run as Background task because of use of PhoneApplicationService.State.</remarks>
        public static void SaveExceptionForReporting(Exception ex)
        {
            SaveExceptionForReporting(ex, VersionManagement.GetVersion().ToString());
        }

        /// <summary>
        /// Report exception.
        /// </summary>
        /// <param name="ex">The exception to report.</param>
        /// <param name="appVersion">Specify app version.</param>
        public static void SaveExceptionForReporting(Exception ex, string appVersion)
        {
            if (ex == null)
            {
                return;
            }

            ExceptionContainer exc = new ExceptionContainer(ex, appVersion);
            PersistentVariables.Set<ExceptionContainer>(Enums.Variables.LittleWatsonExceptionReport.ToString(), exc);
        }

        /// <summary>
        /// Check for previous logged exception.
        /// </summary>
        /// <returns>Return the exception if found.</returns>
        public static ExceptionContainer GetPreviousException()
        {
            // retrieve
            ExceptionContainer exc = PersistentVariables.Get<ExceptionContainer>(Enums.Variables.LittleWatsonExceptionReport.ToString());

            // and clear current exception from 'cache'.
            PersistentVariables.Set<ExceptionContainer>(Enums.Variables.LittleWatsonExceptionReport.ToString(), null);

            return exc;
        }

        /// <summary>
        /// Send error report (exception) to HTTP endpoint.
        /// </summary>
        /// <param name="uri">The Endpoint to report to.</param>
        /// <param name="exception">Exception to send.</param>
        /// <param name="additionalInformation">Additional Information</param>
        public void SendExceptionToHttpEndpoint(string uri, ExceptionContainer exception, string additionalInformation)
        {
            string exceptionString = string.Format("{0}{1}{2}{1}AppVersion: {3}{1}{1}Additional Information: {4}{1}", exception.Message, Environment.NewLine, exception.StackTrace, exception.AppVersion, additionalInformation);
            this.SendExceptionToHttpEndpoint(uri, exceptionString);
        }

        /// <summary>
        /// Send error report (exception) to HTTP endpoint.
        /// </summary>
        /// <param name="uri">The Endpoint to report to.</param>
        /// <param name="exception">Exception to send.</param>
        public void SendExceptionToHttpEndpoint(string uri, ExceptionContainer exception)
        {
            string exceptionString = string.Format("{0}{1}{2}{1}AppVersion: {3}{1}", exception.Message, Environment.NewLine, exception.StackTrace, exception.AppVersion);
            this.SendExceptionToHttpEndpoint(uri, exceptionString);
        }

        /// <summary>
        /// Send custom exception string to HTTP endpoint.
        /// </summary>
        /// <param name="uri">The Endpoint to report to.</param>
        /// <param name="exception">The custom exception string to send.</param>
        public void SendExceptionToHttpEndpoint(string uri, string exception)
        {
            if (!this.AllowAnonymousHttpReporting)
            {
                return;
            }

            try
            {
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
                webRequest.Method = "POST";
                webRequest.ContentType = "application/x-www-form-urlencoded";

                webRequest.BeginGetRequestStream(
                    r =>
                    {
                        try
                        {
                            HttpWebRequest request1 = (HttpWebRequest)r.AsyncState;
                            Stream postStream = request1.EndGetRequestStream(r);

                            string postData = "&exception=" + HttpUtility.UrlEncode(exception);
                            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

                            postStream.Write(byteArray, 0, byteArray.Length);
                            postStream.Close();

                            request1.BeginGetResponse(
                                s =>
                                {
                                    try
                                    {
                                        HttpWebRequest request2 = (HttpWebRequest)s.AsyncState;
                                        HttpWebResponse response = (HttpWebResponse)request2.EndGetResponse(s);

                                        Stream streamResponse = response.GetResponseStream();
                                        StreamReader streamReader = new StreamReader(streamResponse);
                                        string response2 = streamReader.ReadToEnd();
                                        streamResponse.Close();
                                        streamReader.Close();
                                        response.Close();
                                    }
                                    catch
                                    {
                                    }
                                },
                            request1);
                        }
                        catch
                        {
                        }
                    },
                webRequest);
            }
            catch
            {
            }
        }
        #endregion

        #region Private Methods
        private bool GetSetting()
        {
            bool? value = PersistentVariables.Get<bool?>(Enums.Variables.LittleWatsonSettings.ToString());

            // set default
            if (value == null)
            {
                return true;
            }

            return (bool)value;
        }

        private void SetSetting(bool value)
        {
            PersistentVariables.Set<bool>(Enums.Variables.LittleWatsonSettings.ToString(), value);
        }

        private void NotifyPropertyChanged(string name)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }
}
